<template>
  <div class="pages-wrap">
    <div
      ref="waterfallWrap"
      v-loading="isLoading"
      class="waterfall-wrap"
      :style="{ opacity: isFirst ? 0 : 1 }"
    >
      <template v-if="articleList && articleList.length>0">
        <article
          v-for="(item, index) in articleList"
          :key="index"
          :ref="waterfallItem"
          class="waterfall-item"
        >
          <router-link
            :to="`/article/${item.id}`"
            class="a-link"
          >
            <NoHeadImgCpt v-if="!item.head_img" />
            <img
              v-else
              v-lazy:background-image="item.head_img"
              class="head-img"
              :style="{
                height: item.mockImgHeight + 'px',
              }"
            >
            <div class="detail">
              <h3
                class="title"
                :title="item.title"
              >
                {{ item.title }}
              </h3>
              <p
                v-if="item.desc"
                class="desc"
                :title="item.desc"
              >
                {{ item.desc }}
              </p>
              <div class="tag-list">
                <div v-if="item.tags.length">
                  <el-tag
                    v-for="tagItem in item.tags"
                    :key="tagItem.id"
                    class="hss-el-tag"
                    size="small"
                    :disable-transitions="false"
                    :color="tagItem.color"
                  >
                    {{ tagItem.name }}
                  </el-tag>
                </div>
                <span v-else>该文章暂无标签~</span>
              </div>
              <div class="info">
                <img
                  :src="item.users[0] && item.users[0].avatar"
                  class="avatar"
                  alt=""
                >
                <div>
                  <el-icon><View /></el-icon>
                  {{ $filters.convertDate( item.created_at) }}
                </div>
                <div class="relation">
                  <div>
                    <el-icon>
                      <Position />
                    </el-icon>
                    {{ item.click }}
                  </div>
                  <div>
                    <el-icon>
                      <ChatLineRound />
                    </el-icon>
                    {{ item.comment_total }}
                  </div>
                  <div>
                    <el-icon>
                      <Star />
                    </el-icon>
                    {{ item.star_total }}
                  </div>
                </div>
              </div>
            </div>
          </router-link>
        </article>
      </template>
      <div
        ref="loadMoreRef"
        class="load-more-ref"
      />
    </div>
    <SkeletonCpt v-if="hasMore && !articleList.length" />
    <p
      v-if="!hasMore"
      class="no-more"
    >
      已加载所有文章~
    </p>
  </div>
</template>

<script lang="ts">
import SkeletonCpt from '@/components/Skeleton.vue'
import NoHeadImgCpt from '@/components/NoHeadImg/index.vue'
import { typeStore } from '@/store/type'
import { InitData } from '@/types/article'
import { getRandomInt } from '@/utils/index'
import { defineComponent, inject, reactive, toRefs, ref, watch, onUnmounted, onMounted, onBeforeUpdate, nextTick } from 'vue'

export default defineComponent({
  name: 'ArticleView',
  components: { SkeletonCpt, NoHeadImgCpt },
  async setup () {
    const store = typeStore()
    const $get:any = inject('$get')
    const data = reactive(new InitData())

    const loadMoreRef:any = ref<HTMLElement | null>(null)
    const waterfallWrap:any = ref<HTMLElement | null>(null)
    const itemRef:any = ref([])
    const waterfallItem:any = (el: HTMLElement) => {
      // 断言为HTMLElement类型的数组
      (itemRef.value as Array<HTMLElement>).push(el)

      // 这样写编译器会抛出错误
      // --> Argument of type 'HTMLElement' is not assignable to parameter of type 'never'.
      // itemRef.value.push(el)
    }

    data.articleParams.types = store.typeId

    watch(() => data.isBottom, async (newVal, oldVal) => {
      if (data.isLoading) return
      if (newVal && data.hasMore) {
        data.articleParams.nowPage++
        await getArticleList(data.articleParams)
        handleWaterfall()
      }
    })

    watch(() => store.typeId, async (newVal) => {
      data.offsetList = []
      data.articleList = []
      data.articleParams.nowPage = 1
      waterfallWrap.value.style.height = '0px'

      data.articleParams.types = newVal
      await getArticleList(data.articleParams)
      handleWaterfall()
    })

    // 页面加载时
    onMounted(async () => {
      document.title = '首页 - 小锅锅·博客'
      await getArticleList(data.articleParams)
      window.scrollTo({ top: 0 })

      window.onresize = debounce(() => {
        data.offsetList = []
        handleWaterfall()
      }, 100)

      setTimeout(() => { handleWaterfall() }, 100)

      data.isLoading = false
      touchBottom()
    })

    onUnmounted(async () => {
      window.onresize = null
    })

    onBeforeUpdate(() => {
      itemRef.value = []
    })

    const getArticleList = async (params:any) => {
      try {
        data.isLoading = true
        const result = await $get('article/list', params)
        result.rows.forEach((v:any) => {
          const mockImgHeight = getRandomInt(100, 200) + getRandomInt(0, 50)
          v.mockImgHeight = mockImgHeight
        })

        if (params.nowPage === 1) {
          data.articleList = result.rows
        } else {
          data.articleList = [...data.articleList, ...result.rows]
        }

        data.hasMore = result.hasMore
        data.isLoading = false
      } catch (error) {
        console.log(error)
        data.isLoading = false
      }
    }

    // 触底事件
    const touchBottom = () => {
      nextTick(() => {
        const intersectionObserver = new IntersectionObserver((entries) => {
          entries.forEach((change) => {
            // 判断图片是否在可视范围内
            if (!change.isIntersecting) {
              data.isBottom = false
            } else {
              data.isBottom = true
            }
          })
        })
        intersectionObserver.observe(loadMoreRef.value)
      })
    }

    // 防抖
    const debounce = (fun:any, delay:number) => {
      let timer: any = null
      return (args:any) => {
        const that:any = this
        const _args = args
        timer && clearTimeout(timer)
        timer = setTimeout(() => {
          fun.call(that, _args)
        }, delay)
      }
    }

    // 瀑布流定位
    const handleWaterfall = () => {
      // 获取数组最小值的下标
      function getMinIndex (arr:number[]) {
        return [].indexOf.call(arr, Math.min.apply(null, arr))
      }
      // 获取数组最大值的下标
      function getMaxIndex (arr:number[]) {
        return [].indexOf.call(arr, Math.max.apply(null, arr))
      }

      const d = window.pageXOffset || document.documentElement.offsetWidth

      data.waterfallParams.column = d <= 600 ? 1 : d <= 992 ? 2 : 3

      // 列数
      const column = data.waterfallParams.column
      // 间隙
      const gap = data.waterfallParams.gap

      // 瀑布流容器的宽度
      const waterfallWrapWidth:any = window.getComputedStyle(waterfallWrap.value).width
      // 计算减去间隙后，每个item的平均宽度
      const waterfallItemWidth = (waterfallWrapWidth.replace('px', '') - (column - 1) * gap) / column
      waterfallWrap.value.style.position = 'relative'

      if (!itemRef.value) { return false }

      for (let i = (data.articleParams.nowPage - 1) * data.articleParams.pageSize; i < itemRef.value.length; i++) {
        itemRef.value[i].style.position = 'absolute'
        itemRef.value[i].style.width = waterfallItemWidth + 'px'
        if (i < column) {
          data.offsetList.push(itemRef.value[i].offsetHeight) // 第一行不用判断，直接将每个item的offsetHeight保存在数组里

          itemRef.value[i].style.top = '0' // 第一行的top都是0
          if ((i + 1) % column === 1) {
            itemRef.value[i].style.left = 0
          } else {
            const w = i * waterfallItemWidth
            const g = i * gap
            itemRef.value[i].style.left = w + g + 'px'
          }
        } else {
          const minIndex = getMinIndex(data.offsetList)
          const w = minIndex * waterfallItemWidth
          const g = minIndex * gap
          itemRef.value[i].style.top = data.offsetList[minIndex] + gap + 'px' // 计算top
          itemRef.value[i].style.left = w + g + 'px'
          data.offsetList[minIndex] += itemRef.value[i].offsetHeight + gap
        }
      }
      data.isFirst = false
      const maxIndex = getMaxIndex(data.offsetList)
      waterfallWrap.value.style.height = data.offsetList[maxIndex] + 'px'
    }

    const dataRef = toRefs(data)
    return {
      ...dataRef, waterfallWrap, waterfallItem, loadMoreRef
    }
  }
})
</script>

<style lang="scss" scoped>
@import '@/assets/css/constant.scss';
@import '@/assets/css/mixin.scss';

.v-enter {
  opacity: 0;
  transform: translateX(-10px);
}
.v-enter-active {
  transition: all 0.5s ease;
}

/* 响应式布局 - 小于 540px */
@media screen and (max-width: 540px) {
  .pages-wrap {
    .detail {
      .info {
        flex-wrap: wrap;
        justify-content: center !important;
        .avatar {
          margin-right: 10px;
        }
        .relation {
          width: 100%;
          flex: auto !important;
          justify-content: center !important;
          margin-top: 5px;
          & > div {
            margin-right: 10px;
          }
        }
      }
    }
  }
}

@keyframes loading {
  to {
    background-position-x: -50%;
  }
}
.left {
  padding: 0;
}

.pages-wrap {
  .tag-list {
    .el-tag {
      color: $theme-color6;
      border: none;
    }
    .hss-el-tag {
      margin-right: 5px;
      margin-bottom: 3px;
    }
  }

  .waterfall-wrap {
    .waterfall-item {
      overflow: hidden;
      border: 1px solid $theme-color4;
      border-radius: 6px;
      background-color: $theme-color6;
      transition: all 0.5s ease;
      .a-link {
        background-color: $theme-color6;
        color: $theme-color5;
        text-decoration: none;
        display: block;
        .head-img {
          display: inline-block;
          width: 100%;
          background-position: center;
          background-size: cover;
          background-repeat: no-repeat;
          transition: all 0.3s ease 0s;
          &:hover {
            transform: scale(1.1);
          }
        }
        .detail {
          padding: 0 5px 10px;
          .title {
            margin: 10px 0;
            @include multiEllipsis(2);
          }
          .desc {
            @include multiEllipsis(2);
          }
          .tag-list {
            margin: 8px 0;
          }
          .info {
            display: flex;
            align-items: center;
            justify-content: space-between;
            padding: 5px 0;
            font-size: 12px;
            .avatar {
              width: 20px;
              height: 20px;
              border-radius: 50%;
            }
            .relation {
              flex: 0.8;
              display: flex;
              align-items: center;
              justify-content: space-around;
            }
          }
        }
      }
    }
    .load-more-ref {
      position: absolute;
      bottom: 200px;
    }
  }
  .no-more {
    text-align: center;
  }
}
</style>
